package com.cyh;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * 一、线程：
 * 线程的创建方法一, 可以给线程取一个名字 他t1
 *
 * 线程的创建方法二，可以给线程取一个名字 他t2
 * 方法一和二本质是一样的，底层代码调用的也是同一块
 * 方法二的优点：用Runnable更容易与线程池等高级api配合；Runnable脱离Tread继承体系，使用更灵活。
 *
 * lambda简化一下代码
 *
 *
 * 线程的创建方法三：FutureTask配合Thread, 可以返回处理结果
 * 主线程会阻塞，同步等待task3执行完的结果
 *
 * 线程常见方法：
 *    1) start() 启动一个新线程
 *    2) run() 线程启动后运行的方法
 *    3) join() 等待线程运行结束
 *    4) join(long n) 等待线程运行结束，最多等待n毫秒
 *    5) long getId() 获取线程id
 *    6) getName() 获取线程名字
 *    7) setName(String) 修改线程名
 *    8) getPriority() 获取线程的优先级
 *    9) setPriority(int) 修改线程优先级1-10， 效果不一定达到预期，默认5
 *    10) getState() 获取线程的状态：NEW(新建没有运行) RUNNABLE(运行可运行阻塞) BLOCKED(阻塞如没有拿到锁) WAITING(阻塞如join) TIMED_WAITING(阻塞如sleep) TERMINATED(终止)
 *    11) isInterrupted() 判断是否被打断，不会清除打断标记, 打断正常线程返回true, 打断睡眠线程返回false. 用于比较优雅的退出线程。
 *    12) isAlive() 线程是否存活
 *    13) interrupt() 打断sleep wait join的阻塞线程, 打断线程睡眠， 会清空打断状态
 *    14) static interrupted() 判断当前线程是否被打断 会清除打断标记
 *    15) static currentThread() 获取当前正在执行的线程
 *    16) static sleep(long n) 当前线程休眠n毫秒
 *    17) static yield() 提示线程调度器让出当前线程对CPU的使用，等下次时间片过来后，才会运行下面的代码
 *
 *   线程多次调用start()会报错
 *
 *   sleep()和yield()的区别
 *   sleep(): 1) 调用sleep会让当前线程从Running进入Timed Waiting状态（阻塞状态）。
 *            2) 其它线程可以用interrupt()方法打断正在睡眠的线程，这时sleep方法会抛出InterruptedException异常
 *            3) 睡眠结束后的线程未必会立即执行
 *            4) 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
 *    yield(): 1) 调用yield会让当前线程从Running进入Runnable状态（就绪状态），然后调度执行其它同优先级的线程，如果这时没有同优先级的线程，那么不能保证让当前线程暂停效果。
 *             2) 具体的实现依赖于操作系统的任务调度器
 *
 *
 *   子线程中while(true){}循环，cpu占用90%以上， while(true){Thread.sleep()}占用5%左右， 所以要用后面方法防止cpu空转。
 *
 *
 *    杀死线程的错误方法: 调用stop()：会导致锁和资源没有释放。 System.exit(int): 导致整个程序退出
 *    不推荐使用的方法：stop()停止线程，suspend() 挂起（暂停线程） resume()恢复线程： 都容易导致死锁。
 *
 *    LockSupport.park(); 在子线程调用也是阻塞子线程， 在主线程调用t1.interrupt()打断阻塞，子线程再调用park()不会再阻塞，除非在调用前调用Thread.interrupted();
 *
 *    t1.setDaemon(true); 将子线程设为守护线程，表示主线程结束了，子线程也会结束。 垃圾回收器就是守护进程，主进程结束了，垃圾回收器也会结束。
 *
 *
 *
 * */
public class Test01 {
    static int i6 = 0;
    public static void main(String [] args) throws InterruptedException {

        //线程的创建方法一, 可以给线程取一个名字 他t1
        Thread t = new Thread("t1") {
            @Override
            public void run() {
                super.run();
            }
        };
        t.start();

        //线程的创建方法二，可以给线程取一个名字 他t2
        //方法一和二本质是一样的，底层代码调用的也是同一块
        //方法二的优点：用Runnable更容易与线程池等高级api配合；Runnable脱离Tread继承体系，使用更灵活。
        Runnable runnable = new Runnable() {
            @Override
            public void run() {

            }
        };
        Thread t2 = new Thread(runnable, "t2");
        t2.start();

        //lambda简化一下代码
        Runnable r3 = () -> {System.out.println("线程打印的内容");};
        new Thread(r3, "t3").start();


        //线程的创建方法三：FutureTask配合Thread, 可以返回处理结果
        FutureTask<Integer> task3 = new FutureTask<>(() -> {
            System.out.println("第三种线程方法，有返回值");
            return 100;
        });
        new Thread(task3, "t3").start();
        //主线程会阻塞，同步等待task3执行完的结果
        try {
            Integer result = task3.get();
            System.out.println("执行结果是：" + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        //sleep(): 1) 调用sleep会让当前线程从Running进入Timed Waiting状态。
        Thread t4 = new Thread("t4") {
            @Override
            public void run() {
                try {
                    System.out.println("线程状态2---" +  getState());
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t4.start();
        System.out.println("线程状态1---" +  t4.getState());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程状态3---" +  t4.getState());

        //sleep(): 2) 其它线程可以用interrupt()方法打断正在睡眠的线程，这时sleep方法会抛出InterruptedException异常
        Thread t5 = new Thread("t5") {
            @Override
            public void run() {
                try {
                    System.out.println("t5---000---");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    System.out.println("t5---222---");
                    e.printStackTrace();
                }
            }
        };
        t5.start();
        try {
            Thread.sleep(500);
            System.out.println("t5---111---");
            t5.interrupt();
            System.out.println("t5---333---");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //sleep(): 4) 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
        try {
            TimeUnit.SECONDS.sleep(1); //睡眠1秒，效果跟Thread.sleep(1000)一样
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //join()
        Thread t6 = new Thread("t6") {
            @Override
            public void run() {
                try {
                    Thread.sleep(1);
                    i6 = 10;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t6.start();
        t6.join();
        System.out.println("----join() --" + i6); //没有调用join是0， 有是10


        //interrupt()
        Thread t7 = new Thread(() -> {
            sleep(1000);
        }, "t7");
        t7.start();
        sleep(500);
        t7.interrupt();
        System.out.println("当前的打断状态：---" +  t7.isInterrupted()); //false

        //isInterrupted()作用，退出线程中的循环代码
        Thread t8 = new Thread(() -> {
            while(true) {
                if(Thread.currentThread().isInterrupted()) {
                    break;
                }

                sleep(10); //必须放置判断的后面
            }
        }, "t8");
        t7.start();
        sleep(500);
        t8.interrupt();
        System.out.println("当前的打断状态：---" +  t8.isInterrupted()); //true

    }

    public static void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
